package com.hoopchina.weibo.http;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.URL;
import java.net.URLEncoder;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.PartBase;
import org.apache.commons.httpclient.methods.multipart.StringPart;

import android.util.Log;

import com.hoopchina.util.URLEncodeUtils;
import com.hoopchina.weibo.Configuration;
import com.hoopchina.weibo.Weibo;
import com.hoopchina.weibo.WeiboException;
import com.hoopchina.weibo.WeiboType;

/**
 * 
 * @author neusoft
 * 
 */
public class HttpClient implements java.io.Serializable {
	private static final String TAG = "HttpClient";
	private static final int OK = 200;// OK: Success!
	private static final int NOT_MODIFIED = 304;// Not Modified: There was no
												// new data to return.
	private static final int BAD_REQUEST = 400;// Bad Request: The request was
												// invalid. An accompanying
												// error message will explain
												// why. This is the status code
												// will be returned during rate
												// limiting.
	private static final int NOT_AUTHORIZED = 401;// Not Authorized:
													// Authentication
													// credentials were missing
													// or incorrect.
	private static final int FORBIDDEN = 403;// Forbidden: The request is
												// understood, but it has been
												// refused. An accompanying
												// error message will explain
												// why.
	private static final int NOT_FOUND = 404;// Not Found: The URI requested is
												// invalid or the resource
												// requested, such as a user,
												// does not exists.
	private static final int NOT_ACCEPTABLE = 406;// Not Acceptable: Returned by
													// the Search API when an
													// invalid format is
													// specified in the request.
	private static final int INTERNAL_SERVER_ERROR = 500;// Internal Server
															// Error: Something
															// is broken. Please
															// post to the group
															// so the Weibo team
															// can investigate.
	private static final int BAD_GATEWAY = 502;// Bad Gateway: Weibo is down or
												// being upgraded.
	private static final int SERVICE_UNAVAILABLE = 503;// Service Unavailable:
														// The Weibo servers are
														// up, but overloaded
														// with requests. Try
														// again later. The
														// search and trend
														// methods use this to
														// indicate when you are
														// being rate limited.

	private final static boolean DEBUG = Configuration.getDebug();

	private int retryCount = 0;
	private int connectionTimeout = Configuration.getConnectionTimeout();
	private int readTimeout = Configuration.getReadTimeout();
	private static final long serialVersionUID = 808018030183407996L;
	private Map<String, String> requestHeaders = new HashMap<String, String>();
	private OAuth oauth = null;
	private String requestTokenURL = "";
	private String authorizationURL = "";
	private String authenticationURL = "";
	private String accessTokenURL = "";

	private OAuthToken oauthToken = null;

	public HttpClient(int type) {
		switch (type) {
		case WeiboType.SinaType:
			requestTokenURL = "http://api.t.sina.com.cn/oauth/request_token";
			authorizationURL = "http://api.t.sina.com.cn/oauth/authorize";
			authenticationURL = "http://api.t.sina.com.cn/oauth/authenticate";
			accessTokenURL = "http://api.t.sina.com.cn/oauth/access_token";
			break;
		case WeiboType.NetEaseType:
			requestTokenURL = "http://api.t.163.com/oauth/request_token";
			authorizationURL = "http://api.t.163.com/oauth/authorize";
			authenticationURL = "http://api.t.163.com/oauth/authenticate";
			accessTokenURL = "http://api.t.163.com/oauth/access_token";
			break;
		case WeiboType.TencentType:
			requestTokenURL = "http://open.t.qq.com/cgi-bin/request_token";
			authorizationURL = "http://open.t.qq.com/cgi-bin/authorize";
			authenticationURL = "http://open.t.qq.com/cgi-bin/authenticate";
			accessTokenURL = "http://open.t.qq.com/cgi-bin/access_token";
			break;
		case WeiboType.SohuType:
			requestTokenURL = "http://api.t.sohu.com/oauth/request_token";
			authorizationURL = "http://api.t.sohu.com/oauth/authorize";
			authenticationURL = "http://api.t.sohu.com/oauth/authenticate";
			accessTokenURL = "http://api.t.sohu.com/oauth/access_token";
			break;
		}

		setOAuthConsumer(null, null);
		setRequestHeader("Accept-Encoding", "gzip");
	}

	public boolean isAuthenticationEnabled() {
		return null != oauth;
	}
	public void setOAuthConsumer(String consumerKey, String consumerSecret) {
		consumerKey = Configuration.getOAuthConsumerKey(consumerKey);
		consumerSecret = Configuration.getOAuthConsumerSecret(consumerSecret);
		if (null != consumerKey && null != consumerSecret
				&& 0 != consumerKey.length() && 0 != consumerSecret.length()) {
			this.oauth = new OAuth(consumerKey, consumerSecret);
		}
	}

	public RequestToken setToken(String token, String tokenSecret) {
		this.oauthToken = new RequestToken(token, tokenSecret);
		return (RequestToken) this.oauthToken;
	}

	/**
	 * 
	 * @return request token
	 * @throws WeiboException
	 *             tw
	 * @since Weibo4J 1.2.0
	 */
	public RequestToken getOAuthRequestToken(int weiboType)
			throws WeiboException {
		switch (weiboType) {
		case WeiboType.SinaType:
		case WeiboType.TencentType:
			this.oauthToken = new RequestToken(httpRequest2(requestTokenURL,
					new PostParameter[] { new PostParameter("oauth_callback",
							"test") }, true), this);
			break;
		case WeiboType.SohuType:
		case WeiboType.NetEaseType:
			this.oauthToken = new RequestToken(httpRequest(requestTokenURL,
					null, true), this);
			break;
		}
		return (RequestToken) this.oauthToken;
	}

	/**
	 * @param token
	 *            request token
	 * @return access token
	 * @throws WeiboException
	 */
	public RequestToken getOauthRequestToken(String callback_url)
			throws WeiboException {
		this.oauthToken = new RequestToken(httpRequest(requestTokenURL,
				new PostParameter[] { new PostParameter("oauth_callback",
						callback_url) }, true), this);
		return (RequestToken) this.oauthToken;
	}

	/**
	 * @param token
	 *            request token
	 * @return access token
	 * @throws WeiboException
	 */
	public AccessToken getOAuthAccessToken(RequestToken token)
			throws WeiboException {
		try {
			this.oauthToken = token;
			this.oauthToken = new AccessToken(httpRequest(accessTokenURL,
					new PostParameter[0], true));
		} catch (WeiboException te) {
			throw new WeiboException(
					"The user has not given access to the account.", te,
					te.getStatusCode());
		}
		return (AccessToken) this.oauthToken;
	}

	/**
	 * @param token
	 *            request token
	 * @return access token
	 * @throws WeiboException
	 */
	public AccessToken getOAuthAccessToken(int weiboType, RequestToken token,
			String pin) throws WeiboException {
		try {
			switch (weiboType) {
			case WeiboType.SinaType:
			case WeiboType.SohuType:
				this.oauthToken = token;
				this.oauthToken = new AccessToken(httpRequest(accessTokenURL,
						new PostParameter[] { new PostParameter(
								"oauth_verifier", pin) }, true));
				break;
			case WeiboType.TencentType:
				this.oauthToken = token;
				this.oauthToken = new AccessToken(httpRequest2(accessTokenURL,
						new PostParameter[] { new PostParameter(
								"oauth_verifier", pin) }, true));
				break;
			case WeiboType.NetEaseType:
				this.oauthToken = token;
				this.oauthToken = new AccessToken(httpRequest(accessTokenURL,
						new PostParameter[0], true));
				break;
			}
		} catch (WeiboException te) {
//			throw new WeiboException(
//					"The user has not given access to the account.", te,
//					te.getStatusCode());
		}
		return (AccessToken) this.oauthToken;
	}

	public AccessToken getTencentOAuthAccessToken(RequestToken token, String pin)
			throws WeiboException {
		try {
			this.oauthToken = token;
			this.oauthToken = new AccessToken(httpRequest2(accessTokenURL,
					new PostParameter[] { new PostParameter("oauth_verifier",
							pin) }, true));
		} catch (WeiboException te) {
			throw new WeiboException(
					"The user has not given access to the account.", te,
					te.getStatusCode());
		}
		return (AccessToken) this.oauthToken;
	}

	/**
	 * 
	 * @param token
	 *            request token
	 * @param tokenSecret
	 *            request token secret
	 * @return access token
	 * @throws WeiboException
	 * @since Weibo4J 1.2.0
	 */
	public AccessToken getOAuthAccessToken(String token, String tokenSecret)
			throws WeiboException {
		try {
			this.oauthToken = new OAuthToken(token, tokenSecret) {
			};
			this.oauthToken = new AccessToken(httpRequest(accessTokenURL,
					new PostParameter[0], true));
		} catch (WeiboException te) {
			throw new WeiboException(
					"The user has not given access to the account.", te,
					te.getStatusCode());
		}
		return (AccessToken) this.oauthToken;
	}

	/**
	 * 
	 * @param token
	 *            request token
	 * @param tokenSecret
	 *            request token secret
	 * @param oauth_verifier
	 *            oauth_verifier or pin
	 * @return access token
	 * @throws WeiboException
	 * @since Weibo4J 1.2.0
	 */
	public AccessToken getOAuthAccessToken(String token, String tokenSecret,
			String oauth_verifier) throws WeiboException {
		try {
			this.oauthToken = new OAuthToken(token, tokenSecret) {
			};
			this.oauthToken = new AccessToken(httpRequest(accessTokenURL,
					new PostParameter[] { new PostParameter("oauth_verifier",
							oauth_verifier) }, true));
		} catch (WeiboException te) {
			throw new WeiboException(
					"The user has not given access to the account.", te,
					te.getStatusCode());
		}
		return (AccessToken) this.oauthToken;
	}

	/**
	 * Sets the authorized access token
	 * 
	 * @param token
	 *            authorized access token
	 * @since Weibo4J 2.0.0
	 */

	public void setOAuthAccessToken(AccessToken token) {
		this.oauthToken = token;
	}

	public void setRequestTokenURL(String requestTokenURL) {
		this.requestTokenURL = requestTokenURL;
	}

	public String getRequestTokenURL() {
		return requestTokenURL;
	}

	public void setAuthorizationURL(String authorizationURL) {
		this.authorizationURL = authorizationURL;
	}

	public String getAuthorizationURL() {
		return authorizationURL;
	}

	public String getAuthenticationURL() {
		return authenticationURL;
	}

	public void setAuthenticationURL(String authenticationURL) {
		this.authenticationURL = authenticationURL;
	}

	/**
	 * since Weibo4J 1.2.0
	 */
	public String getAuthenticationRL() {
		return authenticationURL;
	}

	public void setAccessTokenURL(String accessTokenURL) {
		this.accessTokenURL = accessTokenURL;
	}

	public String getAccessTokenURL() {
		return accessTokenURL;
	}

	public int getConnectionTimeout() {
		return connectionTimeout;
	}

	/**
	 * Sets a specified timeout value, in milliseconds, to be used when opening
	 * a communications link to the resource referenced by this URLConnection.
	 * System property -Dsinat4j.http.connectionTimeout overrides this
	 * attribute.
	 * 
	 * @param connectionTimeout
	 *            - an int that specifies the connect timeout value in
	 *            milliseconds
	 */
	public void setConnectionTimeout(int connectionTimeout) {
		this.connectionTimeout = Configuration
				.getConnectionTimeout(connectionTimeout);

	}

	public int getReadTimeout() {
		return readTimeout;
	}

	/**
	 * Sets the read timeout to a specified timeout, in milliseconds. System
	 * property -Dsinat4j.http.readTimeout overrides this attribute.
	 * 
	 * @param readTimeout
	 *            - an int that specifies the timeout value to be used in
	 *            milliseconds
	 */
	public void setReadTimeout(int readTimeout) {
		this.readTimeout = Configuration.getReadTimeout(readTimeout);
	}

	public Response post(String url, PostParameter[] postParameters,
			boolean authenticated) throws WeiboException {
		PostParameter[] newPostParameters = new PostParameter[postParameters.length + 1];
		for (int i = 0; i < postParameters.length; i++) {
			newPostParameters[i] = postParameters[i];
		}
		newPostParameters[postParameters.length] = new PostParameter("source",
				Weibo.CONSUMER_KEY);
		return httpRequest(url, newPostParameters, authenticated);
	}

	public Response t163Post(String url, PostParameter[] postParameters,
			boolean authenticated) throws WeiboException {
		PostParameter[] newPostParameters = new PostParameter[postParameters.length];
		for (int i = 0; i < postParameters.length; i++) {
			newPostParameters[i] = postParameters[i];
		}
		return httpRequest(url, newPostParameters, authenticated);
	}

	public Response tencentPost(String url, PostParameter[] postParameters,
			boolean authenticated) throws WeiboException {
		PostParameter[] newPostParameters = new PostParameter[postParameters.length];
		for (int i = 0; i < postParameters.length; i++) {
			newPostParameters[i] = postParameters[i];
		}
		return httpTencentAccessRequest(url, newPostParameters, authenticated);
	}

	public void tencentSyncRequest(String url, String httpMethod,
			String customKey, String customSecret, String requestToken,
			String requestTokenSecrect, String strFormat, String content,
			String clientIP, String pic) throws WeiboException {
	}

	public Response post(String url, String key, String value,
			boolean authenticated) throws WeiboException {
		return post(url, new PostParameter[] { new PostParameter(key, value) },
				authenticated);
	}

	public Response delete(String url, boolean authenticated)
			throws WeiboException {
		return httpRequest(url, null, authenticated, "DELETE");
	}

	public static List<PostParameter> getQueryParameters(String queryString) {
		if (queryString.startsWith("?")) {
			queryString = queryString.substring(1);
		}
		List<PostParameter> result = new ArrayList<PostParameter>();
		if (queryString != null && !queryString.equals("")) {
			String[] p = queryString.split("&");
			for (String s : p) {
				if (s != null && !s.equals("")) {
					if (s.indexOf('=') > -1) {
						String[] temp = s.split("=");
						result.add(new PostParameter(temp[0], temp[1]));
					}
				}
			}
		}

		return result;
	}

	public Response tencentMultPartURL(String url, PostParameter[] params,
			ImageItem item, boolean authenticated) throws WeiboException {
		String params1 = generateParams(url, params, null, true, "POST");
		url = url + "?" + params1;
		List<PostParameter> list = getQueryParameters(params1);
		PostMethod post = new PostMethod(url);
		try {
			org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
			long t = System.currentTimeMillis();
			Part[] parts = null;
			if (params == null) {
				parts = new Part[1];
			} else {
				parts = new Part[list.size() + 1];
			}

			for (int i = 0; i < list.size(); i++) {
				parts[i] = new StringPart(list.get(i).name,
						URLEncodeUtils.decodeURL(list.get(i).value), "UTF-8");
			}

			parts[list.size()] = new ByteArrayPart(item.getContent(),
					item.getName(), item.getImageType());

			post.setRequestEntity(new MultipartRequestEntity(parts, post
					.getParams()));
			List<Header> headers = new ArrayList<Header>();

			if (authenticated) {
				if (oauth == null) {
				}
				String authorization = null;
			}
			client.executeMethod(post);
			Response response = new Response();
			response.setResponseAsString(post.getResponseBodyAsString());
			response.setStatusCode(post.getStatusCode());
			log("multPartURL URL:" + url + ", result:" + response + ", time:"
					+ (System.currentTimeMillis() - t));
			return response;
		} catch (Exception ex) {
			throw new WeiboException(ex.getMessage(), ex, -1);
		} finally {
			post.releaseConnection();
		}
	}

	public Response multPartURL(String url, PostParameter[] params,
			ImageItem item, boolean authenticated) throws WeiboException {
		PostMethod post = new PostMethod(url);
		try {
			org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
			long t = System.currentTimeMillis();
			Part[] parts = null;
			if (params == null) {
				parts = new Part[1];
			} else {
				parts = new Part[params.length + 1];
			}
			if (params != null) {
				int i = 0;
				for (PostParameter entry : params) {
					parts[i++] = new StringPart(entry.getName(),
							(String) entry.getValue());
				}
				parts[parts.length - 1] = new ByteArrayPart(item.getContent(),
						item.getName(), item.getImageType());
			}
			post.setRequestEntity(new MultipartRequestEntity(parts, post
					.getParams()));
			List<Header> headers = new ArrayList<Header>();

			if (authenticated) {
				if (oauth == null) {
				}
				String authorization = null;
				if (null != oauth) {
					// use OAuth
					authorization = oauth.generateAuthorizationHeader("POST",
							url, params, oauthToken);
				} else {
					throw new IllegalStateException(
							"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
				}
				headers.add(new Header("Authorization", authorization));
				log("Authorization: " + authorization);
			}
			client.getHostConfiguration().getParams()
					.setParameter("http.default-headers", headers);
			client.executeMethod(post);

			Response response = new Response();
			response.setResponseAsString(post.getResponseBodyAsString());
			response.setStatusCode(post.getStatusCode());

			log("multPartURL URL:" + url + ", result:" + response + ", time:"
					+ (System.currentTimeMillis() - t));
			return response;
		} catch (Exception ex) {
			throw new WeiboException(ex.getMessage(), ex, -1);
		} finally {
			post.releaseConnection();
		}
	}

	public Response multPartURL(String fileParamName, String url,
			PostParameter[] params, File file, boolean authenticated)
			throws WeiboException {
		PostMethod post = new PostMethod(url);
		org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
		try {
			long t = System.currentTimeMillis();
			Part[] parts = null;
			if (params == null) {
				parts = new Part[1];
			} else {
				parts = new Part[params.length + 1];
			}
			if (params != null) {
				int i = 0;
				for (PostParameter entry : params) {
					parts[i++] = new StringPart(entry.getName(),
							(String) entry.getValue());
				}
			}
			FilePart filePart = new FilePart(fileParamName, file.getName(),
					file, new FileType().getMIMEType(file), "UTF-8");
			filePart.setTransferEncoding("binary");
			parts[parts.length - 1] = filePart;

			post.setRequestEntity(new MultipartRequestEntity(parts, post
					.getParams()));
			List<Header> headers = new ArrayList<Header>();

			if (authenticated) {
				if (oauth == null) {
				}
				String authorization = null;
				if (null != oauth) {
					// use OAuth
					authorization = oauth.generateAuthorizationHeader("POST",
							url, params, oauthToken);
				} else {
					throw new IllegalStateException(
							"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
				}
				headers.add(new Header("Authorization", authorization));
				log("Authorization: " + authorization);
			}
			client.getHostConfiguration().getParams()
					.setParameter("http.default-headers", headers);
			client.executeMethod(post);

			Response response = new Response();
			response.setResponseAsString(post.getResponseBodyAsString());
			response.setStatusCode(post.getStatusCode());

			log("multPartURL URL:" + url + ", result:" + response + ", time:"
					+ (System.currentTimeMillis() - t));
			return response;
		} catch (Exception ex) {
			throw new WeiboException(ex.getMessage(), ex, -1);
		} finally {
			post.releaseConnection();
			client = null;
		}
	}

	private static class ByteArrayPart extends PartBase {
		private byte[] mData;
		private String mName;

		public ByteArrayPart(byte[] data, String name, String type)
				throws IOException {
			super(name, type, "UTF-8", "binary");
			mName = name;
			mData = data;
		}

		protected void sendData(OutputStream out) throws IOException {
			out.write(mData);
		}

		protected long lengthOfData() throws IOException {
			return mData.length;
		}

		protected void sendDispositionHeader(OutputStream out)
				throws IOException {
			super.sendDispositionHeader(out);
			StringBuilder buf = new StringBuilder();
			buf.append("; filename=\"").append(mName).append("\"");
			out.write(buf.toString().getBytes());
		}
	}

	public Response post(String url, boolean authenticated)
			throws WeiboException {
		return httpRequest(url, new PostParameter[0], authenticated);
	}

	public Response post(String url, PostParameter[] PostParameters)
			throws WeiboException {
		return httpRequest(url, PostParameters, false);
	}

	public Response post(String url) throws WeiboException {
		return httpRequest(url, new PostParameter[0], false);
	}

	public Response get(String url, boolean authenticated)
			throws WeiboException {
		return httpRequest(url, null, authenticated);
	}

	public Response get(String url) throws WeiboException {
		return httpRequest(url, null, false);
	}

	public Response httpRequest(String url, PostParameter[] postParams,
			boolean authenticated) throws WeiboException {
		// 统一增加source 参数
		int len = 1;
		PostParameter[] newPostParameters = postParams;
		String method = "GET";
		if (postParams != null) {
			method = "POST";
			len = postParams.length;
			newPostParameters = new PostParameter[len];
			for (int i = 0; i < postParams.length; i++) {
				newPostParameters[i] = postParams[i];
			}
		}
		return httpRequest(url, newPostParameters, authenticated, method);
	}

	public Response httpRequest2(String url, PostParameter[] postParams,
			boolean authenticated) throws WeiboException {
		int len = 1;
		PostParameter[] newPostParameters = postParams;
		String method = "GET";
		if (postParams != null) {
			len = postParams.length;
			newPostParameters = new PostParameter[len];
			for (int i = 0; i < postParams.length; i++) {
				newPostParameters[i] = postParams[i];
			}
		}
		return httpRequest2(url, newPostParameters, authenticated, method);
	}

	protected Response httpTencentAccessRequest(String url,
			PostParameter[] postParams, boolean authenticated)
			throws WeiboException {
		int len = 1;
		PostParameter[] newPostParameters = postParams;
		String method = "GET";
		if (postParams != null) {
			method = "POST";
			len = postParams.length;
			newPostParameters = new PostParameter[len];
			for (int i = 0; i < postParams.length; i++) {
				newPostParameters[i] = postParams[i];
			}
		}
		return httpTencentAccessRequest(url, newPostParameters, authenticated,
				method);
	}

	public Response httpRequest3(String url, PostParameter[] postParams,
			boolean authenticated) throws WeiboException {
		int len = 1;
		PostParameter[] newPostParameters = postParams;
		String method = "GET";
		if (postParams != null) {
			// method = "POST";
			len = postParams.length;
			newPostParameters = new PostParameter[len];
			for (int i = 0; i < postParams.length; i++) {
				newPostParameters[i] = postParams[i];
			}
		}
		return httpRequest3(url, newPostParameters, authenticated, method);
	}

	public Response httpRequest(String url, PostParameter[] postParams,
			boolean authenticated, String httpMethod) throws WeiboException {
		int retriedCount;
		int retry = retryCount + 1;
		Response res = null;
		for (retriedCount = 0; retriedCount < retry; retriedCount++) {
			int responseCode = -1;
				HttpURLConnection con = null;
				OutputStream osw = null;
				try {
					con = getConnection(url);
					con.setDoInput(true);
					con.setConnectTimeout(connectionTimeout);
					setHeaders(url, postParams, con, authenticated, httpMethod);
					if (null != postParams || "POST".equals(httpMethod)) {
						con.setRequestMethod("POST");
						con.setRequestProperty("Content-Type",
								"application/x-www-form-urlencoded");
						con.setDoOutput(true);
						String postParam = "";
						if (postParams != null) {
							postParam = encodeParameters(postParams);
						}
						log("Post Params: ", postParam);
						byte[] bytes = postParam.getBytes("UTF-8");

						con.setRequestProperty("Content-Length",
								Integer.toString(bytes.length));

						osw = con.getOutputStream();
						osw.write(bytes);
						osw.flush();
						osw.close();
					} else if ("DELETE".equals(httpMethod)) {
						con.setRequestMethod("DELETE");
					} else {
						con.setRequestMethod("GET");
					}

					res = new Response(con);
					responseCode = con.getResponseCode();
					if (DEBUG) {
						log("Response: ");
						Map<String, List<String>> responseHeaders = con
								.getHeaderFields();
						for (String key : responseHeaders.keySet()) {
							List<String> values = responseHeaders.get(key);
							for (String value : values) {
								if (null != key) {
									log(key + ": " + value);
								} else {
									log(value);
								}
							}
						}
					}
					if (responseCode != OK) {
						if (responseCode < INTERNAL_SERVER_ERROR
								|| retriedCount == retryCount) {
							Log.i(TAG,"responseCod="+responseCode);
							Log.i(TAG,"responseCod="+getCause(responseCode)+ res.asString());
							throw new WeiboException(res.asString());
						}
						// will retry if the status code is
						// INTERNAL_SERVER_ERROR
					} else {
						break;
					}
				}  catch (IOException ioe) {
					Log.i(TAG,"IOException ioe");
				}
				finally {
					try {
						osw.close();
					} catch (Exception ignore) {
						Log.i(TAG," finally Exception");
					}
				}
		}
		return res;
	}

	public Response httpTencentAccessRequest(String url,
			PostParameter[] postParams, boolean authenticated, String httpMethod)
			throws WeiboException {
		int retriedCount;
		int retry = retryCount + 1;
		Response res = null;
		for (retriedCount = 0; retriedCount < retry; retriedCount++) {
			int responseCode = -1;
			try {
				HttpURLConnection con = null;
				OutputStream osw = null;
				try {
					String params = generateParams(url, postParams, con,
							authenticated, httpMethod);
					// String temp = url + "?" + params;
					con = getConnection(url);
					con.setConnectTimeout(connectionTimeout);
					// for (String key : requestHeaders.keySet()) {
					// con.addRequestProperty(key, requestHeaders.get(key));
					// log(key + ": " + requestHeaders.get(key));
					// }
					con.setDoInput(true);
					if ("POST".equals(httpMethod)) {
						con.setRequestMethod("POST");
						// con.setRequestProperty("Content-Type",
						// "application/x-www-form-urlencoded");
						con.setDoOutput(true);
						String postParam = "";
						if (postParams != null) {
							postParam = params;
						}
						log("Post Params: ", postParam);
						byte[] bytes = postParam.getBytes("UTF-8");

						con.setRequestProperty("Content-Length",
								Integer.toString(bytes.length));

						osw = con.getOutputStream();
						osw.write(bytes);
						osw.flush();
						osw.close();
					} else if ("DELETE".equals(httpMethod)) {
						con.setRequestMethod("DELETE");
					} else {
						con.setRequestMethod("GET");
					}

					res = new Response(con);
					responseCode = con.getResponseCode();
					Log.i("HttpClient","responseCode = "+responseCode);
					Log.i("HttpClient","response = "+res.asString());
					
					if (DEBUG) {
						log("Response: ");
						Map<String, List<String>> responseHeaders = con
								.getHeaderFields();
						for (String key : responseHeaders.keySet()) {
							List<String> values = responseHeaders.get(key);
							for (String value : values) {
								if (null != key) {
									log(key + ": " + value);
								} else {
									log(value);
								}
							}
						}
					}
					if (responseCode != OK) {
						if (responseCode < INTERNAL_SERVER_ERROR
								|| retriedCount == retryCount) {
							throw new WeiboException(getCause(responseCode)
									+ "\n" + res.asString(), responseCode);
						}
						// will retry if the status code is
						// INTERNAL_SERVER_ERROR
					} else {
						break;
					}
				} finally {
					try {
						osw.close();
					} catch (Exception ignore) {
					}
				}
			} catch (IOException ioe) {
				// connection timeout or read timeout
				if (retriedCount == retryCount) {
					throw new WeiboException(ioe.getMessage(), ioe,
							responseCode);
				}
			}
		}
		return res;
	}

	public Response httpRequest2(String url, PostParameter[] postParams,
			boolean authenticated, String httpMethod) throws WeiboException {
		int retriedCount;
		int retry = retryCount + 1;
		Response res = null;
		for (retriedCount = 0; retriedCount < retry; retriedCount++) {
			int responseCode = -1;
			try {
				HttpURLConnection con = null;
				OutputStream osw = null;
				try {
					String params = generateParams(url, postParams, con,
							authenticated, httpMethod);
					con = getConnection(url + "?" + params);
					for (String key : requestHeaders.keySet()) {
						con.addRequestProperty(key, requestHeaders.get(key));
						log(key + ": " + requestHeaders.get(key));
					}
					con.setDoInput(true);
					if ("POST".equals(httpMethod)) {
						con.setRequestMethod("POST");
						con.setRequestProperty("Content-Type",
								"application/x-www-form-urlencoded");
						con.setDoOutput(true);
						String postParam = "";
						if (postParams != null) {
							postParam = encodeParameters(postParams);
						}
						log("Post Params: ", postParam);
						byte[] bytes = postParam.getBytes("UTF-8");

						con.setRequestProperty("Content-Length",
								Integer.toString(bytes.length));

						osw = con.getOutputStream();
						osw.write(bytes);
						osw.flush();
						osw.close();
					} else if ("DELETE".equals(httpMethod)) {
						con.setRequestMethod("DELETE");
					} else {
						con.setRequestMethod("GET");
					}

					res = new Response(con);
					responseCode = con.getResponseCode();
					if (DEBUG) {
						log("Response: ");
						Map<String, List<String>> responseHeaders = con
								.getHeaderFields();
						for (String key : responseHeaders.keySet()) {
							List<String> values = responseHeaders.get(key);
							for (String value : values) {
								if (null != key) {
									log(key + ": " + value);
								} else {
									log(value);
								}
							}
						}
					}
					if (responseCode != OK) {
						if (responseCode < INTERNAL_SERVER_ERROR
								|| retriedCount == retryCount) {
							throw new WeiboException(getCause(responseCode)
									+ "\n" + res.asString(), responseCode);
						}
						// will retry if the status code is
						// INTERNAL_SERVER_ERROR
					} else {
						break;
					}
				} finally {
					try {
						osw.close();
					} catch (Exception ignore) {
					}
				}
			} catch (IOException ioe) {
				// connection timeout or read timeout
				if (retriedCount == retryCount) {
					throw new WeiboException(ioe.getMessage(), ioe,
							responseCode);
				}
			}
		}
		return res;
	}

	public Response httpRequest3(String url, PostParameter[] postParams,
			boolean authenticated, String httpMethod) throws WeiboException {
		int retriedCount;
		int retry = retryCount + 1;
		Response res = null;
		for (retriedCount = 0; retriedCount < retry; retriedCount++) {
			int responseCode = -1;
			try {
				HttpURLConnection con = null;
				OutputStream osw = null;
				try {
					String params = generateParams(url, postParams, con,
							authenticated, httpMethod);
					con = getConnection(url + "&" + params);
					for (String key : requestHeaders.keySet()) {
						con.addRequestProperty(key, requestHeaders.get(key));
						log(key + ": " + requestHeaders.get(key));
					}
					con.setDoInput(true);
					con.setConnectTimeout(connectionTimeout);
					if ("POST".equals(httpMethod)) {
						con.setRequestMethod("POST");
						con.setRequestProperty("Content-Type",
								"application/x-www-form-urlencoded");
						con.setDoOutput(true);
						String postParam = "";
						if (postParams != null) {
							postParam = encodeParameters(postParams);
						}
						log("Post Params: ", postParam);
						byte[] bytes = postParam.getBytes("UTF-8");

						con.setRequestProperty("Content-Length",
								Integer.toString(bytes.length));

						osw = con.getOutputStream();
						osw.write(bytes);
						osw.flush();
						osw.close();
					} else if ("DELETE".equals(httpMethod)) {
						con.setRequestMethod("DELETE");
					} else {
						con.setRequestMethod("GET");
					}

					res = new Response(con);
					responseCode = con.getResponseCode();
					if (DEBUG) {
						log("Response: ");
						Map<String, List<String>> responseHeaders = con
								.getHeaderFields();
						for (String key : responseHeaders.keySet()) {
							List<String> values = responseHeaders.get(key);
							for (String value : values) {
								if (null != key) {
									log(key + ": " + value);
								} else {
									log(value);
								}
							}
						}
					}
					if (responseCode != OK) {
						if (responseCode < INTERNAL_SERVER_ERROR
								|| retriedCount == retryCount) {
							throw new WeiboException(getCause(responseCode)
									+ "\n" + res.asString(), responseCode);
						}
						// will retry if the status code is
						// INTERNAL_SERVER_ERROR
					} else {
						break;
					}
				} finally {
					try {
						osw.close();
					} catch (Exception ignore) {
					}
				}
			} catch (IOException ioe) {
				// connection timeout or read timeout
				if (retriedCount == retryCount) {
					throw new WeiboException(ioe.getMessage(), ioe,
							responseCode);
				}
			}
		}
		return res;
	}

	public static String encodeParameters(PostParameter[] postParams) {
		StringBuffer buf = new StringBuffer();
		for (int j = 0; j < postParams.length; j++) {
			if (j != 0) {
				buf.append("&");
			}
			try {
				buf.append(URLEncoder.encode(postParams[j].name, "UTF-8"))
						.append("=")
						.append(URLEncoder.encode(postParams[j].value, "UTF-8"));
			} catch (java.io.UnsupportedEncodingException neverHappen) {
			}
		}
		return buf.toString();

	}

	/**
	 * sets HTTP headers
	 * 
	 * @param connection
	 *            HttpURLConnection
	 * @param authenticated
	 *            boolean
	 */
	private void setHeaders(String url, PostParameter[] params,
			HttpURLConnection connection, boolean authenticated,
			String httpMethod) {
		log("Request: ");
		log(httpMethod + " ", url);

		if (authenticated) {
			if (oauth == null) {
			}
			String authorization = null;
			if (null != oauth) {
				// use OAuth
				authorization = oauth.generateAuthorizationHeader(httpMethod,
						url, params, oauthToken);
			} else {
				throw new IllegalStateException(
						"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
			}
			connection.addRequestProperty("Authorization", authorization);
			log("Authorization: " + authorization);
		}
		for (String key : requestHeaders.keySet()) {
			connection.addRequestProperty(key, requestHeaders.get(key));
			log(key + ": " + requestHeaders.get(key));
		}
	}

	private String generateParams(String url, PostParameter[] params,
			HttpURLConnection connection, boolean authenticated,
			String httpMethod) {
		log("Request: ");
		log(httpMethod + " ", url);
		String authorization = null;
		if (authenticated) {
			if (oauth == null) {
			}

			if (null != oauth) {
				// use OAuth
				authorization = oauth.generateTencentAuthorizationHeader(
						httpMethod, url, params, oauthToken);
			} else {
				throw new IllegalStateException(
						"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
			}
			log("Authorization: " + authorization);
		}

		return authorization;
	}

	public void setRequestHeader(String name, String value) {
		requestHeaders.put(name, value);
	}

	public String getRequestHeader(String name) {
		return requestHeaders.get(name);
	}

	private HttpURLConnection getConnection(String url) throws IOException {
		HttpURLConnection con = null;
		con = (HttpURLConnection) new URL(url).openConnection();
		if (connectionTimeout > 0) {
			con.setConnectTimeout(connectionTimeout);
		}
		if (readTimeout > 0) {
			con.setReadTimeout(readTimeout);
		}
		return con;
	}

	private static void log(String message) {
		if (DEBUG) {
			System.out.println("[" + new java.util.Date() + "]" + message);
		}
	}

	private static void log(String message, String message2) {
		if (DEBUG) {
			log(message + message2);
		}
	}

	private static String getCause(int statusCode) {
		String cause = null;
		switch (statusCode) {
		case NOT_MODIFIED:
			break;
		case BAD_REQUEST:
			cause = "The request was invalid.  An accompanying error message will explain why. This is the status code will be returned during rate limiting.";
			break;
		case NOT_AUTHORIZED:
			cause = "Authentication credentials were missing or incorrect.";
			break;
		case FORBIDDEN:
			cause = "The request is understood, but it has been refused.  An accompanying error message will explain why.";
			break;
		case NOT_FOUND:
			cause = "The URI requested is invalid or the resource requested, such as a user, does not exists.";
			break;
		case NOT_ACCEPTABLE:
			cause = "Returned by the Search API when an invalid format is specified in the request.";
			break;
		case INTERNAL_SERVER_ERROR:
			cause = "Something is broken.  Please post to the group so the Weibo team can investigate.";
			break;
		case BAD_GATEWAY:
			cause = "Weibo is down or being upgraded.";
			break;
		case SERVICE_UNAVAILABLE:
			cause = "Service Unavailable: The Weibo servers are up, but overloaded with requests. Try again later. The search and trend methods use this to indicate when you are being rate limited.";
			break;
		default:
			cause = "";
		}
		return statusCode + ":" + cause;
	}
}
